// OpenGLView.cpp : implementation of the COpenGLView class
//

#include "stdafx.h"
#include "TerrainEditor.h"

#include "TerrainEditorDoc.h"
#include "OpenGLView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// COpenGLView

IMPLEMENT_DYNCREATE(COpenGLView, CView)

BEGIN_MESSAGE_MAP(COpenGLView, CView)
	//{{AFX_MSG_MAP(COpenGLView)
	ON_WM_CREATE()
	ON_WM_ERASEBKGND()
	ON_WM_DESTROY()
	ON_WM_SIZE()
	ON_WM_SETCURSOR()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// COpenGLView construction/destruction

COpenGLView::COpenGLView()
{
	m_hMouseCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
}

COpenGLView::~COpenGLView()
{
}

BOOL COpenGLView::PreCreateWindow(CREATESTRUCT& cs)
{
	// An OpenGL window has its own pixel format. Because of this, only device 
	// contexts retrieved for the client area of an OpenGL window are allowed 
	// to draw into the window. As a result, an OpenGL window should be created 
	// with the WS_CLIPCHILDREN and WS_CLIPSIBLINGS styles.

	cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;

	// The style CS_HREDRAW and CS_VREDRAW force the Window to redraw whenever 
	// it is resized. CS_OWNDC creates a private DC for the Window. Meaning the 
	// DC is not shared across applications.

	cs.lpszClass = AfxRegisterWndClass(CS_OWNDC | CS_HREDRAW | CS_VREDRAW);

	return CView::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// COpenGLView drawing

void COpenGLView::OnDraw(CDC* pDC)
{
	CTerrainEditorDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	// Force OnDraw to complete before next OnDraw

	static BOOL bDrawing = FALSE;
	if (bDrawing) return;
	bDrawing = TRUE;

	// Make the DC the target RC

	wglMakeCurrent(m_pCDC->GetSafeHdc(), m_hRC);

	// Clear buffers

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// Call virtual drawing function

	VirtualOnDraw();

	// The glFinish function does not return until the effects of all previously 
	// called OpenGL functions are complete. Such effects include all changes to 
	// the OpenGL state, all changes to the connection state, and all changes to 
	// the framebuffer contents.

	glFinish();

	// If double buffering is used then swap the buffers

	SwapBuffers(m_pCDC->GetSafeHdc());
	
	// Free the target DC

	wglMakeCurrent(0, 0);

	// Finished with OnDraw

	bDrawing = FALSE;
}


/////////////////////////////////////////////////////////////////////////////
// COpenGLView diagnostics

#ifdef _DEBUG
void COpenGLView::AssertValid() const
{
	CView::AssertValid();
}

void COpenGLView::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}

CTerrainEditorDoc* COpenGLView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CTerrainEditorDoc)));
	return (CTerrainEditorDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// COpenGLView message handlers

BOOL COpenGLView::SetupPixelFormat()
{
	// Generic PFD that can be user configured

	m_pfdPixelFormat.nSize				= sizeof(PIXELFORMATDESCRIPTOR);
	m_pfdPixelFormat.nVersion			= 1;
	m_pfdPixelFormat.dwFlags			= PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_GENERIC_ACCELERATED | PFD_DOUBLEBUFFER;
	m_pfdPixelFormat.iPixelType			= PFD_TYPE_RGBA;
	m_pfdPixelFormat.cColorBits			= 32;
	m_pfdPixelFormat.cRedBits			= 0;
	m_pfdPixelFormat.cRedShift			= 0;
	m_pfdPixelFormat.cGreenBits			= 0;
	m_pfdPixelFormat.cGreenShift		= 0;
	m_pfdPixelFormat.cBlueBits			= 0;
	m_pfdPixelFormat.cBlueShift			= 0;
	m_pfdPixelFormat.cAlphaBits			= 8;
	m_pfdPixelFormat.cAlphaShift		= 0;
	m_pfdPixelFormat.cAccumBits			= 0;
	m_pfdPixelFormat.cAccumRedBits		= 0;
	m_pfdPixelFormat.cAccumGreenBits	= 0;
	m_pfdPixelFormat.cAccumBlueBits		= 0;
	m_pfdPixelFormat.cAccumAlphaBits	= 0;
	m_pfdPixelFormat.cDepthBits			= 24;
	m_pfdPixelFormat.cStencilBits		= 8;
	m_pfdPixelFormat.cAuxBuffers		= 0;
	m_pfdPixelFormat.iLayerType			= PFD_MAIN_PLANE;
	m_pfdPixelFormat.bReserved			= 0;
	m_pfdPixelFormat.dwLayerMask		= 0;
	m_pfdPixelFormat.dwVisibleMask		= 0;
	m_pfdPixelFormat.dwDamageMask		= 0;

	// Apply user configuration changes

	// Search through the DC supported pixel formats for a best match to the
	// user configured pixel format.

    ASSERT(m_pCDC != 0);
    int pixel_format;

	if ((pixel_format = ChoosePixelFormat(m_pCDC->GetSafeHdc(), &m_pfdPixelFormat)) == 0)
    {
        AfxMessageBox("Could not find a best match to the supplied pixel format");
        return FALSE;
    }

	// Set the DC pixel format to the format specified. If unable to do so, we will
	// fall back on a generic format that the DC can support and inform user.

	if (SetPixelFormat(m_pCDC->GetSafeHdc(), pixel_format, &m_pfdPixelFormat) == FALSE)
    {
        AfxMessageBox("Unable to set the specified pixel format");
        pixel_format = 1;	
		if (DescribePixelFormat(m_pCDC->GetSafeHdc(), pixel_format, sizeof(PIXELFORMATDESCRIPTOR), &m_pfdPixelFormat) == 0)
		{
			AfxMessageBox("OpenGL compatible pixel format not supported");
			return FALSE;
		}
    }

	return TRUE;
}

int COpenGLView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CView::OnCreate(lpCreateStruct) == -1) return -1;
	
	// Initialize DC

	m_pCDC = new CClientDC(this);
	if (m_pCDC == 0) return (-1);

	// Win32 uses device contexts (DC) to support graphics rendering. OpenGL
	// uses rendering contexts (RC) to support graphics rendering. In order to
	// render correctly, OpenGL must create a RC from a suitable DC. The first
	// step in this creation is to set the pixel format of the drawing surface.
	// Once completed, a RC can be created from this information.

    if (!SetupPixelFormat()) return (-2);

	// Create a new OpenGL rendering context, which is suitable for drawing on 
	// the device referenced by m_pCDC. The rendering context has the same pixel 
	// format as the device context.

    m_hRC = wglCreateContext(m_pCDC->GetSafeHdc());
	if (m_hRC == 0) return (-3);

	// Make the specified OpenGL rendering context the calling thread's current 
	// rendering context. All subsequent OpenGL calls made by the thread are drawn 
	// on the device identified by m_pCDC. You can also use wglMakeCurrent to change 
	// the calling thread's current rendering context so it's no longer current.

    if (wglMakeCurrent(m_pCDC->GetSafeHdc(), m_hRC) == FALSE) return (-4);

	// Get support for all available OpenGL extensions

	m_extOpenGL.GetExtensions();

	// OpenGL init commands

	VirtualOnCreate();

	// Free the target window DC

    if (wglMakeCurrent(0, 0) == FALSE) return (-5);
	
	return 0;
}

BOOL COpenGLView::OnEraseBkgnd(CDC* pDC) 
{
	// OpenGL handles all background erasing capability so tell Win32 to skip

	return TRUE;
}

void COpenGLView::OnDestroy() 
{
	CView::OnDestroy();

	// Makes the calling thread's current RC no longer current, and releases
	// the DC that is used by the RC

    wglMakeCurrent(m_pCDC->GetSafeHdc(), 0);

	// Delete the specified OpenGL rendering context

	if (m_hRC != 0) 
	{
		wglDeleteContext(m_hRC);
		m_hRC = 0;
	}

	// Delete the DC pointer

	if (m_pCDC != 0) 
	{
		delete m_pCDC;
		m_pCDC = 0;
	}
}

void COpenGLView::OnSize(UINT nType, int cx, int cy) 
{
	CView::OnSize(nType, cx, cy);
	
	// Assure a non-zero window size
	
	if ( 0 >= cx || 0 >= cy || nType == SIZE_MINIMIZED) return;

	// Specify the target DeviceContext of the subsequent OGL calls

	wglMakeCurrent(m_pCDC->GetSafeHdc(), m_hRC);

	// Call the virtual sizing procedure (overridden by other view classes)

	VirtualOnSize(cx, cy);

	// Free the target DeviceContext

	wglMakeCurrent(0, 0);

	// Force redraw

	Invalidate(TRUE);	
}

BOOL COpenGLView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
{
	ASSERT(m_hMouseCursor != NULL);
	SetCursor(m_hMouseCursor);
	return CView::OnSetCursor(pWnd, nHitTest, message);
}

void COpenGLView::BeginOpenGLCommands() // used to make OpenGL calls in functions that are not virtual
{
	// Are we in a current DC and RC?

	if (!(m_hRC == wglGetCurrentContext() && m_pCDC->GetSafeHdc() == wglGetCurrentDC()))
	{
		// If not, set the DC...

		wglMakeCurrent(m_pCDC->GetSafeHdc(), m_hRC);
		m_bUsingOpenGLCommands = true;
	};
}

void COpenGLView::EndOpenGLCommands()  // used to make OpenGL calls in functions that are not virtual
{
	if (m_bUsingOpenGLCommands) wglMakeCurrent(0, 0);
}

void COpenGLView::DrawOpenGLCoordinateSystem()
{
	BeginOpenGLCommands();
	
	glBegin(GL_LINES);
		// +X Axis
		glColor3f(1.f,0.f,0.f);
		glVertex3f(0.0f,0.0f,0.0f);
		glVertex3f(50.0f,0.0f,0.0f);
		// +Y Axis
		glColor3f(0.f,1.f,0.f);
		glVertex3f(0.0f,0.0f,0.0f);
		glVertex3f(0.0f,50.0f,0.0f);
		// +Z Axis
		glColor3f(0.f,0.f,1.f);
		glVertex3f(0.0f,0.0f,0.0f);
		glVertex3f(0.0f,0.0f,50.0f);
		// -X Axis
		glColor3f(1.f,1.f,0.f);
		glVertex3f(0.0f,0.0f,0.0f);
		glVertex3f(-50.0f,0.0f,0.0f);
		// -Y Axis
		glColor3f(0.f,1.f,1.f);
		glVertex3f(0.0f,0.0f,0.0f);
		glVertex3f(0.0f,-50.0f,0.0f);
		// -Z Axis
		glColor3f(1.f,0.f,1.f);
		glVertex3f(0.0f,0.0f,0.0f);
		glVertex3f(0.0f,0.0f,-50.0f);
	glEnd();

	EndOpenGLCommands();
}


void COpenGLView::VirtualOnCreate()
{
}

void COpenGLView::VirtualOnDraw()
{
}

void COpenGLView::VirtualOnSize(int width, int height)
{
}
